﻿using CPAIS.Model;
using CPAIS.Updater;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace CPAIS.LogParser
{
    public enum ParseMode
    { 
        UsingXmlDocument,
        UsingXmlReader
    }

    public class LogParserFactory
    {
        private Dictionary<string, Type> parserTable;
        private ParseMode mode;

        /// <summary>
        /// Gets and sets the parsing mode.
        /// </summary>
        public ParseMode Mode
        {
            get { return mode; }
            set
            {
                if (value == ParseMode.UsingXmlReader)
                {
                    // Removes the log parser which is the subclass of ILogParserWithXmlDocument.
                    foreach (var xmlTag in parserTable.Keys)
                    {
                        var parser = parserTable[xmlTag];
                        if (parser.IsSubclassOf(typeof(ILogParserWithXmlDocument)))
                            parserTable.Remove(xmlTag);
                    }
                }
                else if (value == ParseMode.UsingXmlDocument)
                {

                    // Removes the log parser which is the subclass of ILogParserWithXmlReader.
                    foreach (var xmlTag in parserTable.Keys)
                    {
                        var parser = parserTable[xmlTag];
                        if (parser.IsSubclassOf(typeof(ILogParserWithXmlReader)))
                            parserTable.Remove(xmlTag);
                    }
                }
            }
        }

        /// <summary>
        /// By default, we will add the Parser using XmlReader.
        /// </summary>
        public LogParserFactory(ParseMode mode, bool addDefault = true)
        {
            this.parserTable = new Dictionary<string, Type>();
            this.mode = mode;

            if (addDefault)
            {
                if (mode == ParseMode.UsingXmlReader)
                {
                    AddLogParser(typeof(PluginsWithXmlReader.LogParserA));
                    AddLogParser(typeof(PluginsWithXmlReader.LogParserB));
                    AddLogParser(typeof(PluginsWithXmlReader.LogParserC));
                    AddLogParser(typeof(PluginsWithXmlReader.LogParserD));
                }
                else
                {
                    AddLogParser(typeof(PluginsWithXmlDocument.LogParserA));
                    AddLogParser(typeof(PluginsWithXmlDocument.LogParserB));
                    AddLogParser(typeof(PluginsWithXmlDocument.LogParserC));
                }
            }
        }

        /// <summary>
        /// Adds an log parser to the factory.
        /// </summary>
        public void AddLogParser(Type type)
        {
            if (type.GetInterface("CPAIS.LogParser.ILogParserWithXmlReader") != null)
            {
                ILogParserWithXmlReader parser = Activator.CreateInstance(type) as ILogParserWithXmlReader;
                parserTable.Add(parser.XmlTag, type);
            }
            else if (type.GetInterface("CPAIS.LogParser.ILogParserWithXmlDocument") != null)
            {
                ILogParserWithXmlDocument parser = Activator.CreateInstance(type) as ILogParserWithXmlDocument;
                parserTable.Add(parser.XmlTag, type);
            }
            else
                throw new ArgumentException("Not an invalid parser type", "type");
        }

        /// <summary>
        /// Removes the log parser with specified type
        /// </summary>
        public void RemoveLogParser(Type type)
        {
            if (type.GetInterface("CPAIS.LogParser.ILogParserWithXmlReader") != null)
            {
                ILogParserWithXmlReader parser = Activator.CreateInstance(type) as ILogParserWithXmlReader;
                parserTable.Remove(parser.XmlTag);
            }
            else if (type.GetInterface("CPAIS.LogParser.ILogParserWithXmlDocument") != null)
            {
                ILogParserWithXmlDocument parser = Activator.CreateInstance(type) as ILogParserWithXmlDocument;
                parserTable.Remove(parser.XmlTag);
            }
            else
                throw new ArgumentException("Not an invalid parser type", "type");
        }

        /// <summary>
        /// Parses event log file.
        /// </summary>
        public LogModel ParseLog(string pathName, StateUpdater updater)
        {
            if (updater != null)
                updater.Begin();

            try
            {
                LogModel logModel = new LogModel(pathName.Substring(pathName.LastIndexOf('\\') + 1));

                if (mode == ParseMode.UsingXmlReader)
                {
                    ILogParserWithXmlReader parser = null;
                    XmlReader reader = new XmlTextReader(pathName);
                    while (reader.Read())
                    {
                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            if (parserTable.ContainsKey(reader.Name))
                            {
                                parser = Activator.CreateInstance(parserTable[reader.Name]) as ILogParserWithXmlReader;
                                break;
                            }
                        }
                    }
                    if (parser == null)
                        throw new Exception("There are no event log parser plugins to open it!");
                    else
                        parser.Parse(logModel, reader, updater);
                }
                else if (mode == ParseMode.UsingXmlDocument)
                {
                    ILogParserWithXmlDocument parser = null;
                    XmlDocument xmlDoc = new XmlDocument();
                    xmlDoc.Load(pathName);
                    XmlNode rootNode = xmlDoc.FirstChild;
                    while (rootNode != null && parser == null)
                    {
                        if (parserTable.ContainsKey(rootNode.Name))
                        {
                            parser = Activator.CreateInstance(parserTable[rootNode.Name]) as ILogParserWithXmlDocument;
                            break;
                        }
                        else
                            rootNode = rootNode.NextSibling;
                    }
                    if (parser == null)
                        throw new Exception("There are no event log parser plugins to open it!");
                    else
                        parser.Parse(logModel, rootNode, updater);
                }

                logModel.Valid = true;
                if (updater != null)
                    updater.Complete();
                return logModel;
            }
            catch (Exception ex)
            {
                if (updater != null)
                {
                    if (updater.State != UpdateState.Cancelled)
                        updater.Fail(ex.Message);
                    return null;
                }
                else
                    throw ex;
            }
        }
    }
}